﻿using Sidvall.Data;
using System.Collections.Generic;
using System.Xml.Linq;

namespace Sidvall.Serialization
{
    public class SerializerManager
    {
        #region Public Members

        public const string NumberSeparator = ";";

        public ISerializer Serializer { get; set; }

        #region Deserialize

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public object Deserialize(string value)
        {
            return this.Serializer.Deserialize(value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public object Deserialize(string value, SerializerSettings settings)
        {
            return this.Serializer.Deserialize(value, settings);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public T Deserialize<T>(string value)
        {
            return this.Serializer.Deserialize<T>(value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public T Deserialize<T>(string value, SerializerSettings settings)
        {
            return this.Serializer.Deserialize<T>(value, settings);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public object Deserialize(string value, Sidvall.Data.DataType type)
        {
            switch (type)
            {
                case Sidvall.Data.DataType.Boolean:
                    return value.ToNullableBoolean();
                case Sidvall.Data.DataType.Char:
                    return value.ToNullableChar();
                case Sidvall.Data.DataType.DateTime:
                    return value.ToNullableDateTime(System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None);
                case Sidvall.Data.DataType.DateTimeOffset:
                    return value.ToNullableDateTimeOffset(System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None);
                case Sidvall.Data.DataType.Decimal:
                    return value.ToNullableDecimal();
                case Sidvall.Data.DataType.Double:
                    return value.ToNullableDouble();
                case Sidvall.Data.DataType.Byte:
                    return value.ToNullableByte();
                case Sidvall.Data.DataType.Int16:
                    return value.ToNullableInt16();
                case Sidvall.Data.DataType.Int32:
                    return value.ToNullableInt32();
                case Sidvall.Data.DataType.Int64:
                    return value.ToNullableInt64();
                case Sidvall.Data.DataType.Single:
                    return value.ToNullableSingle();
                case Sidvall.Data.DataType.String:
                    return value;
                case Sidvall.Data.DataType.TimeSpan:
                    return value.ToNullableTimeSpan(System.Globalization.CultureInfo.InvariantCulture);
                case Sidvall.Data.DataType.Guid:
                    return value.ToNullableGuid();
                case Sidvall.Data.DataType.Xml:
                    return value;
                case Sidvall.Data.DataType.Binary:
                    if (value == null)
                        return null;
                    return System.Text.Encoding.UTF8.GetBytes(value);
                case Sidvall.Data.DataType.Geography:
                    return Sidvall.Data.Geography.Deserialize(value);
                case Sidvall.Data.DataType.Geometry:
                    return Sidvall.Data.Geometry.Deserialize(value);
                default:
                    throw new System.NotImplementedException(type.ToString() + " not implemented!");
            }
        }

        #endregion
        #region DeserializeArray

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public object DeserializeArray(string value, Sidvall.Data.DataType type)
        {
            string[] items;

            if (string.IsNullOrWhiteSpace(value))
                return null;
            switch (type)
            {
                case Sidvall.Data.DataType.Decimal:
                    items = value.Split(new string[] { NumberSeparator }, System.StringSplitOptions.RemoveEmptyEntries);
                    var decimalArray = new System.Collections.Generic.List<decimal>();
                    foreach (var item in items)
                    {
                        var itemValue = item.ToNullableDecimal();
                        if (itemValue != null)
                            decimalArray.Add(itemValue.Value);
                    }
                    return decimalArray.ToArray();
                case Sidvall.Data.DataType.Double:
                    items = value.Split(new string[] { NumberSeparator }, System.StringSplitOptions.RemoveEmptyEntries);
                    var doubleArray = new System.Collections.Generic.List<double>();
                    foreach (var item in items)
                    {
                        var itemValue = item.ToNullableDouble();
                        if (itemValue != null)
                            doubleArray.Add(itemValue.Value);
                    }
                    return doubleArray.ToArray();
                case Sidvall.Data.DataType.Byte:
                    items = value.Split(new string[] { NumberSeparator }, System.StringSplitOptions.RemoveEmptyEntries);
                    var int8Array = new System.Collections.Generic.List<byte>();
                    foreach (var item in items)
                    {
                        var itemValue = item.ToNullableByte();
                        if (itemValue != null)
                            int8Array.Add(itemValue.Value);
                    }
                    return int8Array.ToArray();
                case Sidvall.Data.DataType.Int16:
                    items = value.Split(new string[] { NumberSeparator }, System.StringSplitOptions.RemoveEmptyEntries);
                    var int16Array = new System.Collections.Generic.List<short>();
                    foreach (var item in items)
                    {
                        var itemValue = item.ToNullableInt16();
                        if (itemValue != null)
                            int16Array.Add(itemValue.Value);
                    }
                    return int16Array.ToArray();
                case Sidvall.Data.DataType.Int32:
                    items = value.Split(new string[] { NumberSeparator }, System.StringSplitOptions.RemoveEmptyEntries);
                    var int32Array = new System.Collections.Generic.List<int>();
                    foreach (var item in items)
                    {
                        var itemValue = item.ToNullableInt32();
                        if (itemValue != null)
                            int32Array.Add(itemValue.Value);
                    }
                    return int32Array.ToArray();
                case Sidvall.Data.DataType.Int64:
                    items = value.Split(new string[] { NumberSeparator }, System.StringSplitOptions.RemoveEmptyEntries);
                    var int64Array = new System.Collections.Generic.List<long>();
                    foreach (var item in items)
                    {
                        var itemValue = item.ToNullableInt64();
                        if (itemValue != null)
                            int64Array.Add(itemValue.Value);
                    }
                    return int64Array.ToArray();
                case Data.DataType.String:
                    return Deserialize<string[]>(value);
                case Data.DataType.Guid:
                    return Deserialize<System.Guid[]>(value);
                default:
                    throw new System.NotImplementedException(type.ToString() + " not implemented!");
            }
        }

        #endregion
        #region DeserializeXml

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public XDocument DeserializeXml(string value)
        {
            return DeserializeXml(value, null);
        }
        public XDocument DeserializeXml(string value, string deserializeRootElementName)
        {
            return DeserializeXml(value, deserializeRootElementName, false);
        }
        public XDocument DeserializeXml(string value, string deserializeRootElementName, bool writeArrayAttribute)
        {
            return this.Serializer.DeserializeXml(value, deserializeRootElementName, writeArrayAttribute);
        }

        #endregion

        #region Serialize

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(bool value)
        {
            return value.ToString();
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(char value)
        {
            return value.ToString();
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(System.DateTime value)
        {
            return value.ToString("s", System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(System.DateTimeOffset value)
        {
            return value.ToString("u", System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(decimal value)
        {
            return value.ToString(System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(double value)
        {
            return value.ToString(System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(byte value)
        {
            return value.ToString(System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(short value)
        {
            return value.ToString(System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(int value)
        {
            return value.ToString(System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(long value)
        {
            return value.ToString(System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(float value)
        {
            return value.ToString(System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(string value)
        {
            return value;
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(byte[] value)
        {
            if (value == null)
                return null;
            if (value.Length == 0)
                return null;
            return System.Text.Encoding.UTF8.GetString(value, 0, value.Length);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(System.TimeSpan value)
        {
            return value.ToString("c", System.Globalization.CultureInfo.InvariantCulture);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(System.Guid value)
        {
            return value.ToString("D");
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(Sidvall.Data.Geography value)
        {
            if (value == null)
                return null;
            return value.Serialize();
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string Serialize(Sidvall.Data.Geometry value)
        {
            if (value == null)
                return null;
            return value.Serialize();
        }

        #endregion
        #region SerializeArray

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeArray(System.Collections.Generic.IEnumerable<decimal> value)
        {
            return string.Join<decimal>(NumberSeparator, value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeArray(System.Collections.Generic.IEnumerable<double> value)
        {
            return string.Join<double>(NumberSeparator, value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeArray(System.Collections.Generic.IEnumerable<byte> value)
        {
            return string.Join<byte>(NumberSeparator, value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeArray(System.Collections.Generic.IEnumerable<short> value)
        {
            return string.Join<short>(NumberSeparator, value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeArray(System.Collections.Generic.IEnumerable<int> value)
        {
            return string.Join<int>(NumberSeparator, value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeArray(System.Collections.Generic.IEnumerable<long> value)
        {
            return string.Join<long>(NumberSeparator, value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeArray(System.Collections.Generic.IEnumerable<string> value)
        {
            return this.Serializer.Serialize(value);
        }
        public string SerializeArray(System.Collections.Generic.IEnumerable<System.Guid> value)
        {
            return this.Serializer.Serialize(value);
        }

        #endregion
        #region SerializeBusinessObject

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeBusinessObject(object value, Formatting formatting, bool ignoreServiceDataRowState)
        {
            return SerializeBusinessObject(value, formatting, ignoreServiceDataRowState, null, DisplayPositionPolicy.None, null);
        }
        public string SerializeBusinessObject(object value, Formatting formatting, bool ignoreServiceDataRowState, IEnumerable<PropertyItem> propertyItems,
            DisplayPositionPolicy defaultDisplayPositionPolicy, int? defaultDisplayPosition)
        {
            var settings = new SerializerSettings()
            {
                Formatting = formatting,
                DefaultDisplayPositionPolicy = defaultDisplayPositionPolicy,
                DefaultDisplayPosition = defaultDisplayPosition,
            };
            if (ignoreServiceDataRowState)
            {
                settings.PropertyItems.AddExcludedItem(nameof(ServiceDataRowBase.DataRowContext));
                settings.PropertyItems.AddExcludedItem(nameof(ServiceDataRowCollectionItem<ServiceDataRowBase>.DeletedItemKeys));
            }
            settings.PropertyItems.AddRange(propertyItems);
            return this.Serializer.Serialize(value, settings);
        }

        #endregion
        #region SerializeObject

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeObject(object value)
        {
            return this.Serializer.Serialize(value);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeObject(object value, Formatting formatting)
        {
            var settings = new SerializerSettings()
            {
                Formatting = formatting,
            };
            return this.Serializer.Serialize(value, settings);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string SerializeObject(object value, SerializerSettings settings)
        {
            return this.Serializer.Serialize(value, settings);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "Sidvall.Serialization.ObjectExtension.ToNullableTimeSpan(System.Object)")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        public string SerializeObject(object value, Sidvall.Data.DataType type)
        {
            switch (type)
            {
                case Sidvall.Data.DataType.Boolean:
                    bool? boolValue = value.ToNullableBoolean();
                    if (boolValue == null)
                        return null;
                    return Serialize(boolValue.Value);
                case Sidvall.Data.DataType.Char:
                    char? charValue = value.ToNullableChar();
                    if (charValue == null)
                        return null;
                    return Serialize(charValue.Value);
                case Sidvall.Data.DataType.DateTime:
                    System.DateTime? dateTimeValue = value.ToNullableDateTime();
                    if (dateTimeValue == null)
                        return null;
                    return Serialize(dateTimeValue.Value);
                case Sidvall.Data.DataType.DateTimeOffset:
                    System.DateTimeOffset? dateTimeOffsetValue = value.ToNullableDateTimeOffset();
                    if (dateTimeOffsetValue == null)
                        return null;
                    return Serialize(dateTimeOffsetValue.Value);
                case Sidvall.Data.DataType.Decimal:
                    decimal? decimalValue = value.ToNullableDecimal();
                    if (decimalValue == null)
                        return null;
                    return Serialize(decimalValue.Value);
                case Sidvall.Data.DataType.Double:
                    double? doubleValue = value.ToNullableDouble();
                    if (doubleValue == null)
                        return null;
                    return Serialize(doubleValue.Value);
                case Sidvall.Data.DataType.Byte:
                    byte? byteValue = value.ToNullableByte();
                    if (byteValue == null)
                        return null;
                    return Serialize(byteValue.Value);
                case Sidvall.Data.DataType.Int16:
                    short? int16Value = value.ToNullableInt16();
                    if (int16Value == null)
                        return null;
                    return Serialize(int16Value.Value);
                case Sidvall.Data.DataType.Int32:
                    int? int32Value = value.ToNullableInt32();
                    if (int32Value == null)
                        return null;
                    return Serialize(int32Value.Value);
                case Sidvall.Data.DataType.Int64:
                    long? int64Value = value.ToNullableInt64();
                    if (int64Value == null)
                        return null;
                    return Serialize(int64Value.Value);
                case Sidvall.Data.DataType.Single:
                    float? floatValue = value.ToNullableSingle();
                    if (floatValue == null)
                        return null;
                    return Serialize(floatValue.Value);
                case Sidvall.Data.DataType.String:
                    return value.ToString(null);
                case Sidvall.Data.DataType.TimeSpan:
                    System.TimeSpan? timeSpanValue = value.ToNullableTimeSpan();
                    if (timeSpanValue == null)
                        return null;
                    return Serialize(timeSpanValue.Value);
                case Sidvall.Data.DataType.Guid:
                    System.Guid? guidValue = value.ToNullableGuid();
                    if (guidValue == null)
                        return null;
                    return Serialize(guidValue.Value);
                case Sidvall.Data.DataType.Xml:
                    return value.ToString(null);
                case Sidvall.Data.DataType.Binary:
                    return Serialize(value as byte[]);
                case Sidvall.Data.DataType.Geography:
                    Sidvall.Data.Geography geographyValue = value as Sidvall.Data.Geography;
                    if (geographyValue == null)
                        return null;
                    return Serialize(geographyValue);
                case Sidvall.Data.DataType.Geometry:
                    Sidvall.Data.Geometry geometryValue = value as Sidvall.Data.Geometry;
                    if (geometryValue == null)
                        return null;
                    return Serialize(geometryValue);
                default:
                    throw new System.NotImplementedException(type.ToString() + " not implemented!");
            }
        }

        #endregion
        #region SerializeBusinessObject

        public string SerializeXml(XNode node)
        {
            return SerializeXml(node, false);
        }
        public string SerializeXml(XNode node, bool omitRootObject)
        {
            return SerializeXml(node, omitRootObject, null);
        }
        public string SerializeXml(XNode node, bool omitRootObject, SerializerSettings settings)
        {
            return this.Serializer.SerializeXml(node, omitRootObject, settings);
        }

        #endregion

        #region TryDeserialize

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public T TryDeserialize<T>(string value, System.Func<T> getDefaultValueHandler)
        {
            return this.Serializer.TryDeserialize<T>(value, getDefaultValueHandler);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public T TryDeserialize<T>(string value, System.Func<T> getDefaultValueHandler, SerializerSettings settings)
        {
            return this.Serializer.TryDeserialize<T>(value, getDefaultValueHandler, settings);
        }

        #endregion

        #endregion
        #region Factory Members

        #region Current

        private static readonly System.Lazy<SerializerManager> _Current = new System.Lazy<SerializerManager>(GetSerializerManager);
        public static SerializerManager Current
        {
            get
            {
                return _Current.Value;
            }
        }

        #endregion

        #endregion
        #region Private Members

        #region GetSerializerManager

        private static SerializerManager GetSerializerManager()
        {
            return new SerializerManager(new Sidvall.Serialization.Serializer());
        }

        #endregion

        #endregion
        #region Constructors

        public SerializerManager(ISerializer serializer)
        {
            this.Serializer = serializer;
        }

        #endregion
    }
}
